package com.github.cyf.common.excel.entity;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.github.cyf.common.excel.ExcelUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * 单元格合并策略
 * @author chenyifan
 * @create 2024-01-11 20:30
 */
public class CellMergeStrategy implements CellWriteHandler {

    /**
     * 合并列配置列表
     */
    private List<MergeConfig> mergeColumnConfigList;

    /**
     * 当前读取到的配置列表索引
     */
    private int configIndex;

    private CellMergeStrategy(List<MergeConfig> mergeColumnConfigList) {
        this.mergeColumnConfigList = mergeColumnConfigList;
        this.configIndex = 0;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {

        private List<MergeConfig> mergeColumnConfigList;

        public Builder() {
            this.mergeColumnConfigList = new ArrayList<>();
        }

        /**
         * 添加合并配置
         * @param config
         * @return
         */
        public Builder addConfig(MergeConfig config) {
            if (config.getBeginRow() == null || config.getEndRow() == null || config.getBeginCol() == null || config.getEndCol() == null) {
                throw new RuntimeException("合并配置行列索引有空值，" +  config);
            }
            // 如果合并区间是一个单元格，是不需要合并的
            if (!isCell(config.getBeginRow(), config.getEndRow(), config.getBeginCol(), config.getEndCol())) {
                mergeColumnConfigList.add(config);
            }
            return this;
        }

        public CellMergeStrategy build() {
            // 排序合并配置列表，先比较行，行相等再比较列
            mergeColumnConfigList.sort(Comparator.comparingInt(MergeConfig::getEndRow).thenComparingInt(MergeConfig::getEndCol));
            return new CellMergeStrategy(mergeColumnConfigList);
        }

        /**
         * 判断合并策略中的开始行，开始列，结束行，结束列表示的是否是单元格
         * @param beginRow
         * @param endRow
         * @param beginCol
         * @param endCol
         * @return
         */
        private boolean isCell(int beginRow, int endRow, int beginCol, int endCol) {
            return beginRow == endRow && beginCol == endCol;
        }
    }

    /**
     * 调用时机：在完成一个单元格的数据写入并准备释放资源后被调用
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cellDataList
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        if (configIndex >= mergeColumnConfigList.size()) {
            return;
        }
        int curRowIndex = cell.getRowIndex();
        int curColIndex = cell.getColumnIndex();
        MergeConfig config = mergeColumnConfigList.get(configIndex);
        if (curRowIndex == config.getEndRow() && curColIndex == config.getEndCol()) {
            // 向上合并单元格
            mergeWithPrevRow(cell.getSheet(), config);
            this.configIndex++;
        }
    }

    /**
     * 根据合并配置，向上合并单元格
     * @param sheet
     * @param config  合并配置
     */
    private static void mergeWithPrevRow(Sheet sheet, MergeConfig config) {
        // 首先移除合并区域的单元格
        for (int i = config.getBeginRow(); i <= config.getEndRow(); i++) {
            Row row = sheet.getRow(i);
            for (int j = config.getBeginCol(); j <= config.getEndCol(); j++) {
                Cell cell = row.getCell(j);
                row.removeCell(cell);
            }
        }
        CellRangeAddress cellRangeAddress = new CellRangeAddress(config.getBeginRow(), config.getEndRow(), config.getBeginCol(), config.getEndCol());
        // 在单元格范围内填充内容
        ExcelUtil.fillCellRange(sheet, cellRangeAddress, config.getMergeInfo());
    }

}
